#include "gy-39.h"

#if GY_39_MODE == GY_39_HARDWARE
/**
 * @name 等待就绪
 * @description
 * @function
 * @param {uint32_t} event 事件
 * @returns {void}
 * @requires [event]
 */
static void _gy39_waitI2C(const uint32_t event) {
    uint32_t timeout = GY_39_Timeout;
    while (I2C_CheckEvent(GY_39_I2C, event) == ERROR && timeout > 0)
        --timeout;
}
#elif GY_39_MODE == GY_39_SOFTWARE
/**
 * @name 开始信号
 * @description
 * @function
 * @returns {void}
 * @requires []
 */
static void _gy39_start(void) {
    // 输出模式
    GY_39_SDA_OUT();
    // 拉高时钟
    GY_39_SCL_Set();
    // 拉高数据
    GY_39_SDA_Set();
    // 等待休眠
    delay_us(GY_39_Sleep);
    // 拉低数据
    GY_39_SDA_Clr();
    // 等待休眠
    delay_us(GY_39_Sleep);
    // 拉低时钟
    GY_39_SCL_Clr();
    // 等待休眠
    delay_us(GY_39_Sleep);
}

/**
 * @name 结束信号
 * @description
 * @function
 * @returns {void}
 * @requires []
 */
static void _gy39_stop(void) {
    // 输出模式
    GY_39_SDA_OUT();
    // 拉低时钟
    GY_39_SCL_Clr();
    // 拉低数据
    GY_39_SDA_Clr();
    // 等待休眠
    delay_us(GY_39_Sleep);
    // 拉高时钟
    GY_39_SCL_Set();
    // 等待休眠
    delay_us(GY_39_Sleep);
    // 拉高数据
    GY_39_SDA_Set();
    // 等待休眠
    delay_us(GY_39_Sleep);
}

/**
 * @name ACK应答
 * @description
 * @function
 * @param {uint8_t} ack 是否产生
 * @returns {void}
 * @requires [ack]
 */
static void _gy39_ack(const uint8_t ack) {
    // 输出模式
    GY_39_SDA_OUT();
    // 拉低时钟
    GY_39_SCL_Clr();
    // 发送响应
    if (ack == GY_39_ACK)
        GY_39_SDA_Clr();
    else
        GY_39_SDA_Set();
    // 等待休眠
    delay_us(GY_39_Sleep);
    // 拉高时钟
    GY_39_SCL_Set();
    // 等待休眠
    delay_us(GY_39_Sleep);
    // 拉低时钟
    GY_39_SCL_Clr();
    // 等待休眠
    delay_us(GY_39_Sleep);
}

/**
 * @name 等待就绪
 * @description
 * @function
 * @returns {void}
 * @requires []
 */
static uint8_t _gy39_waitI2C(void) {
    uint32_t timeout = 0;
    // 输入模式
    GY_39_SDA_IN();
    // 拉低时钟
    GY_39_SCL_Clr();
    // 拉高数据
    GY_39_SDA_Set();
    // 等待休眠
    delay_us(GY_39_Sleep);
    // 拉高时钟
    GY_39_SCL_Set();
    // 等待休眠
    delay_us(GY_39_Sleep);
    // 等待就绪
    while (GY_39_SDA_READ())
        if (++timeout > GY_39_Timeout) {
            _gy39_stop();
            return 1;
        }
    // 等待休眠
    delay_us(GY_39_Sleep);
    return 0;
}

/**
 * @name 发送数据
 * @description
 * @function
 * @param {uint8_t} ch 数据
 * @returns {void}
 * @requires [ch]
 */
static void _gy39_send(const uint8_t ch) {
    uint8_t bit = 0x80;
    // 输出模式
    GY_39_SDA_OUT();
    // 发送数据
    while (bit) {
        // 拉低时钟
        GY_39_SCL_Clr();
        // 等待休眠
        delay_us(GY_39_Sleep);
        // 发送一位
        if (ch & bit)
            GY_39_SDA_Set();
        else
            GY_39_SDA_Clr();
        // 等待休眠
        delay_us(GY_39_Sleep);
        // 拉高时钟
        GY_39_SCL_Set();
        // 等待休眠
        delay_us(GY_39_Sleep);
        // 下一个位
        bit >>= 1;
    }
}

/**
 * @name 接收数据
 * @description
 * @function
 * @param {uint8_t} ack 是否响应
 * @returns {uint8_t}
 * @requires []
 */
static uint8_t _gy39_recv(const uint8_t ack) {
    uint8_t i, receive = 0;
    // 输入模式
    GY_39_SDA_IN();
    // 接收数据
    for (i = 0; i < 8; ++i) {
        // 拉低时钟
        GY_39_SCL_Clr();
        // 等待休眠
        delay_us(GY_39_Sleep);
        // 拉高时钟
        GY_39_SCL_Set();
        // 等待休眠
        delay_us(GY_39_Sleep);
        // 接收数据
        receive <<= 1;
        if (GY_39_SDA_READ())
            ++receive;
    }
    // 发送响应
    _gy39_ack(ack);
    // 返回数据
    return receive;
}
#endif

/**
 * @name 写入数据
 * @description
 * @function
 * @param {uint8_t} ch 数据
 * @param {uint8_t} addr 地址
 * @returns {void}
 * @requires [ch,addr]
 */
static void _gy39_writeI2C(const uint8_t ch, const uint8_t addr) {
#if GY_39_MODE == GY_39_HARDWARE
    // 开始信号
    I2C_GenerateSTART(GY_39_I2C, ENABLE);
    // 等待就绪
    _gy39_waitI2C(I2C_EVENT_MASTER_MODE_SELECT);
    // 从机地址
    I2C_Send7bitAddress(GY_39_I2C, GY_39_ID + 0, I2C_Direction_Transmitter);
    // 等待就绪
    _gy39_waitI2C(I2C_EVENT_MASTER_TRANSMITTER_MODE_SELECTED);
    // 写入地址
    I2C_SendData(GY_39_I2C, addr);
    // 等待就绪
    _gy39_waitI2C(I2C_EVENT_MASTER_BYTE_TRANSMITTED);
    // 写入数据
    I2C_SendData(GY_39_I2C, ch);
    // 等待就绪
    _gy39_waitI2C(I2C_EVENT_MASTER_BYTE_TRANSMITTED);
    // 停止信号
    I2C_GenerateSTOP(GY_39_I2C, ENABLE);
#elif GY_39_MODE == GY_39_SOFTWARE
    // 开始信号
    _gy39_start();
    // 从机地址
    _gy39_send(GY_39_ID + 0);
    // 等待就绪
    _gy39_waitI2C();
    // 写入地址
    _gy39_send(addr);
    // 等待就绪
    _gy39_waitI2C();
    // 写入数据
    _gy39_send(ch);
    // 等待就绪
    _gy39_waitI2C();
    // 停止信号
    _gy39_stop();
#endif
}

/**
 * @name 读取数据
 * @description
 * @function
 * @param {uint8_t} addr 地址
 * @returns {uint8_t} 数据
 * @requires [addr]
 */
static uint8_t _gy39_readI2C(const uint8_t addr) {
#if GY_39_MODE == GY_39_HARDWARE
    uint8_t ans;
    // 开始信号
    I2C_GenerateSTART(GY_39_I2C, ENABLE);
    // 等待就绪
    _gy39_waitI2C(I2C_EVENT_MASTER_MODE_SELECT);
    // 从机地址
    I2C_Send7bitAddress(GY_39_I2C, GY_39_ID + 0, I2C_Direction_Transmitter);
    // 等待就绪
    _gy39_waitI2C(I2C_EVENT_MASTER_TRANSMITTER_MODE_SELECTED);
    // 读取地址
    I2C_SendData(GY_39_I2C, addr);
    // 等待就绪
    _gy39_waitI2C(I2C_EVENT_MASTER_BYTE_TRANSMITTED);
    // 开始信号
    I2C_GenerateSTART(GY_39_I2C, ENABLE);
    // 等待就绪
    _gy39_waitI2C(I2C_EVENT_MASTER_MODE_SELECT);
    // 从机地址
    I2C_Send7bitAddress(GY_39_I2C, GY_39_ID + 1, I2C_Direction_Receiver);
    // 等待就绪
    _gy39_waitI2C(I2C_EVENT_MASTER_RECEIVER_MODE_SELECTED);
    // 禁用应答
    I2C_AcknowledgeConfig(GY_39_I2C, DISABLE);
    // 停止信号
    I2C_GenerateSTOP(GY_39_I2C, ENABLE);
    // 等待就绪
    _gy39_waitI2C(I2C_EVENT_MASTER_BYTE_RECEIVED);
    // 读取数据
    ans = I2C_ReceiveData(GY_39_I2C);
    // 启用应答
    I2C_AcknowledgeConfig(GY_39_I2C, ENABLE);
    // 返回数据
    return ans;
#elif GY_39_MODE == GY_39_SOFTWARE
    uint8_t ans;
    // 开始信号
    _gy39_start();
    // 从机地址
    _gy39_send(GY_39_ID + 0);
    // 等待就绪
    _gy39_waitI2C();
    // 读取地址
    _gy39_send(addr);
    // 等待就绪
    _gy39_waitI2C();
    // 开始信号
    _gy39_start();
    // 从机地址
    _gy39_send(GY_39_ID + 1);
    // 等待就绪
    _gy39_waitI2C();
    // 接收数据
    ans = _gy39_recv(0);
    // 停止信号
    _gy39_stop();
    // 返回数据
    return ans;
#endif
}

/**
 * @name gy-39初始化
 * @description
 * @function
 * @returns {void}
 * @requires []
 */
void gy39_init(void) {
#if GY_39_MODE == GY_39_HARDWARE
    I2C_InitTypeDef I2C_InitStruct = {0};
    GPIO_InitTypeDef GPIO_InitStruct = {0};
    // 使能时钟
    GY_39_RCC_AHBPeriphClockCmd;
    // 配置GPIO
    GPIO_InitStruct.GPIO_Pin = GY_39_GPIO_Pin;
    GPIO_InitStruct.GPIO_Mode = GPIO_Mode_AF;
    GPIO_InitStruct.GPIO_OType = GPIO_OType_OD;
    GPIO_InitStruct.GPIO_Speed = GPIO_Speed_50MHz;
    GPIO_InitStruct.GPIO_PuPd = GPIO_PuPd_NOPULL;
    // 初始GPIO
    GPIO_Init(GY_39_GPIO, &GPIO_InitStruct);
    // 配置复用
    GPIO_PinAFConfig(GY_39_GPIO, GY_39_I2C_SCL, GY_39_GPIO_AF_I2C);
    GPIO_PinAFConfig(GY_39_GPIO, GY_39_I2C_SDA, GY_39_GPIO_AF_I2C);
    // 使能时钟
    GY_39_RCC_APBPeriphClockCmd;
    // 配置I2C
    I2C_InitStruct.I2C_ClockSpeed = GY_39_I2C_ClockSpeed;
    I2C_InitStruct.I2C_Mode = I2C_Mode_I2C;
    I2C_InitStruct.I2C_DutyCycle = I2C_DutyCycle_2;
    I2C_InitStruct.I2C_OwnAddress1 = GY_39_HOST_ID;
    I2C_InitStruct.I2C_Ack = I2C_Ack_Enable;
    I2C_InitStruct.I2C_AcknowledgedAddress = I2C_AcknowledgedAddress_7bit;
    // 初始I2C
    I2C_Init(GY_39_I2C, &I2C_InitStruct);
    // 使能I2C
    I2C_Cmd(GY_39_I2C, ENABLE);
#elif GY_39_MODE == GY_39_SOFTWARE
    GPIO_InitTypeDef GPIO_InitStruct = {0};
    // 使能时钟
    GY_39_RCC_AHBPeriphClockCmd;
    // 配置GPIO
    GPIO_InitStruct.GPIO_Pin = GY_39_GPIO_Pin;
    GPIO_InitStruct.GPIO_Mode = GPIO_Mode_OUT;
    GPIO_InitStruct.GPIO_OType = GPIO_OType_OD;
    GPIO_InitStruct.GPIO_Speed = GPIO_Speed_50MHz;
    GPIO_InitStruct.GPIO_PuPd = GPIO_PuPd_NOPULL;
    // 初始GPIO
    GPIO_Init(GY_39_GPIO, &GPIO_InitStruct);
    // 拉高时钟
    GY_39_SCL_Set();
    // 拉高数据
    GY_39_SDA_Set();
#endif
}

/**
 * @name 接收数据
 * @description
 * @function
 * @returns {void}
 * @requires []
 */
GY_39_ENV gy39_recv(void) {
    GY_39_ENV env = {0};
    // 读取照度
    env.Lux |= (uint32_t)_gy39_readI2C(0x00) << 24;
    env.Lux |= (uint32_t)_gy39_readI2C(0x01) << 16;
    env.Lux |= (uint32_t)_gy39_readI2C(0x02) << 8;
    env.Lux |= (uint32_t)_gy39_readI2C(0x03);
    // 读取温度
    env.Tem |= (uint16_t)_gy39_readI2C(0x04) << 8;
    env.Tem |= (uint16_t)_gy39_readI2C(0x05);
    // 读取气压
    env.Atm |= (uint32_t)_gy39_readI2C(0x06) << 24;
    env.Atm |= (uint32_t)_gy39_readI2C(0x07) << 16;
    env.Atm |= (uint32_t)_gy39_readI2C(0x08) << 8;
    env.Atm |= (uint32_t)_gy39_readI2C(0x09);
    // 读取湿度
    env.Hum |= (uint16_t)_gy39_readI2C(0x0a) << 8;
    env.Hum |= (uint16_t)_gy39_readI2C(0x0b);
    // 读取海拔
    env.Alt |= (uint16_t)_gy39_readI2C(0x0c) << 8;
    env.Alt |= (uint16_t)_gy39_readI2C(0x0d);
    // 返回环境
    return env;
}

/**
 * @name 发送数据
 * @description
 * @function
 * @param {uint8_t} ch 数据
 * @param {uint8_t} addr 地址
 * @returns {void}
 * @requires [ch,addr]
 */
void gy39_send(const uint8_t ch, const uint8_t addr) {
    _gy39_writeI2C(ch, addr);
}